/*********************************************************************************
 *      Copyright:  (C) 2022 guoyi 
 *                  All rights reserved.
 *
 *       Filename:  util_proc.c
 *    Description:  This file is the process API
 *                 
 *        Version:  1.0.0(7/07/2021)
 *         Author:  guoyi <675088383@qq.com>
 *      ChangeLog:  1, Release initial version on "7/07/2020 09:19:02 PM"
 *                 
 ********************************************************************************/ 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>

#include "util_proc.h"
#include "logger.h"


/* ****************************************************************************
 * FunctionName: record_daemon_pid
 * Description : Record the running daemon program PID to the file "pid_file"
 * Inputs      : pid_file:The record PID file path
 * Output      : NONE
 * Return      : 0: Record successfully  Else: Failure
 * *****************************************************************************/
int record_daemon_pid(const char *pid_file)
{ 
    struct stat fStatBuf; 
    int fd = -1; 
    int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU;
    char ipc_dir[64] = { 0 }; 
    
    strncpy(ipc_dir, pid_file, 64); 

    /* dirname() will modify ipc_dir and save the result */ 
    dirname(ipc_dir);  
    
    /* If folder pid_file PATH doesnot exist, then we will create it" */
    if (stat(ipc_dir, &fStatBuf) < 0) 
    { 
        if (mkdir(ipc_dir, mode) < 0) 
        { 
            log_err("cannot create %s: %s\n", ipc_dir, strerror(errno)); 
            return -1; 
        } 
        
        (void)chmod(ipc_dir, mode); 
    } 
    
    /*  Create the process running PID file */ 
    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0)
    {
        char pid[PID_ASCII_SIZE]; 
        snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid()); 
        write(fd, pid, strlen(pid)); 
        close(fd); 

        log_dbg("Record PID<%u> to file %s.\n", getpid(), pid_file);
    } 
    else 
    {
        log_err("cannot create %s: %s\n", pid_file, strerror(errno));
        return -1;
    }

    return 0;
}

/* ****************************************************************************
 * FunctionName: get_daemon_pid
 * Description : Get the daemon process PID from the PID record file "pid_file"
 * Inputs      : pid_file: the PID record file
 * Output      : NONE
 * Return      : pid_t: The daemon process PID number
 * *****************************************************************************/
pid_t get_daemon_pid(const char *pid_file)
{ 
    FILE *f; 
    pid_t pid; 
    
    if ((f = fopen(pid_file, "rb")) != NULL)
    { 
        char pid_ascii[PID_ASCII_SIZE]; 
        (void)fgets(pid_ascii, PID_ASCII_SIZE, f); 
        (void)fclose(f); 
        pid = atoi(pid_ascii); 
    } 
    else
    {
        log_err("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
        return -1;
    } 
    return pid;
}     

/* ****************************************************************************
 * FunctionName: check_daemon_running
 * Description : Check the daemon program already running or not
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int check_daemon_running(const char *pid_file)
{
    int rv = -1; 
    struct stat fStatBuf;

    rv = stat(pid_file, &fStatBuf); 
    if (0 == rv) 
    { 
        pid_t pid = -1; 
        printf("PID record file \"%s\" exist.\n", pid_file);

        pid = get_daemon_pid(pid_file);
        if (pid > 0)  /*  Process pid exist */
        { 
            if ((rv = kill(pid, 0)) == 0) 
            { 
                printf("Program with PID[%d] seems running.\n", pid); 
                return 1; 
            } 
            else   /* Send signal to the old process get no reply. */ 
            { 
                printf("Program with PID[%d] seems exit.\n", pid); 
                remove(pid_file); 
                return 0; 
            } 
        } 
        else if (0 == pid) 
        { 
            printf("Can not read program PID form record file.\n"); 
            remove(pid_file); 
            return 0; 
        } 
        else  /* Read pid from file "pid_file" failure */
        { 
            printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file); 
            return 1; 
        } 
    } 
    
    return 0;
}

/* ****************************************************************************
 * FunctionName: stop_daemon_running
 * Description : Stop the daemon program running
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int stop_daemon_running(const char *pid_file)
{
    pid_t            pid = -1; 
    struct stat      fStatBuf;

    if ( stat(pid_file, &fStatBuf) < 0)
        return 0;

    printf("PID record file \"%s\" exist.\n", pid_file); 
    pid = get_daemon_pid(pid_file);
    if (pid > 0)  /*  Process pid exist */
    { 
        while ( (kill(pid, 0) ) == 0) 
        { 
            kill(pid, SIGTERM);
            sleep(1);
        } 
        
        remove(pid_file); 
    } 
    
    return 0;
}


/* ****************************************************************************
 * FunctionName: set_daemon_running
 * Description : Set the programe running as daemon if it's not running and record 
 *               its PID to the pid_file.
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 0: Successfully. 1: Failure
 * *****************************************************************************/
int set_daemon_running(const char *pid_file)
{ 
    daemon(0, 1); 
    log_nrml("Program running as daemon [PID:%d].\n", getpid()); 
    
    if (record_daemon_pid(pid_file) < 0) 
    { 
        log_err("Record PID to file \"%s\" failure.\n", pid_file); 
        return -2;
    }

    return 0;
}

/* excute a linux command by system() */
void exec_system_cmd(const char *format, ...)
{
    char                cmd[256];
    va_list             args;
    
    memset(cmd, 0, sizeof(cmd)); 
    
    va_start(args, format); 
    vsnprintf(cmd, sizeof(cmd), format, args);
    va_end(args); 
    
    system(cmd);
}


